<!-- 当前页面代码实现了小程序开发者怎么通过栖岛提供的标准方法来获取授权登录 -->
<!-- 为了方便当前代码简称小程序，栖岛端简称宿主 -->
<template>
	<view class="container">
		<view class="header">
			<text class="title">栖岛 小程序 授权演示</text>
			<text class="subtitle">简洁流畅的授权体验示例</text>
		</view>

		<view class="note">
			<text class="note-text">📋 说明：此页面用于测试小程序内部的OAuth授权功能。</text>
			<text class="note-detail">🌐 小程序端：使用uni.requireNativePlugin("OAuthDialog")标准方式调用</text>
			<text class="note-detail">💡 参数说明：小程序只需传递client_id，其他数据由宿主自动获取</text>
		</view>
		
		<view class="user-card">
			<image class="avatar" :src="(userInfo && userInfo.user && userInfo.user.avatar) || '/static/logo.png'" mode="aspectFill"></image>
			<view class="user-meta">
				<text class="user-name">{{ (userInfo && userInfo.user && userInfo.user.screenName) || '未登录' }}</text>
				<text class="user-id">UID：{{ (userInfo && userInfo.user && userInfo.user.uid) || '-' }}</text>
			</view>
		</view>
		
		

		<view class="card">
			<view class="form-group">
				<text class="label">客户端 ID</text>
				<input class="input" v-model="clientId" placeholder="请输入客户端ID" type="text" :disabled="isLoading" />
				<text class="example">默认值：test_client</text>
			</view>

			<view class="hint">
				<text class="hint-title">使用说明</text>
				<text class="hint-line">- 授权成功后自动获取用户信息</text>
				<text class="hint-line">- 可随时点击按钮重新获取</text>
			</view>
		</view>

		<view class="actions">
			<button class="btn primary" @click="testOAuth" :disabled="isLoading">
				{{ isLoading ? '登录中…' : '立即登录' }}
			</button>
			<button class="btn ghost" @click="getUserInfo" :disabled="loading">
				{{ loading ? '获取中…' : '获取用户信息' }}
			</button>
			<button class="btn subtle" @click="clearResult">清除</button>
			<button class="btn subtle" @click="fillDefault">默认值</button>
		</view>

		<!-- 结果显示区域 -->
		<view class="card" v-if="result">
			<view class="result-header">
				<text class="result-title" :class="result.success ? 'ok' : 'err'">{{ result.success ? '授权成功' : '授权失败' }}</text>
				<text class="result-time" v-if="resultTime">{{ resultTime }}</text>
			</view>

			<view class="result-content">
				<view v-if="result.success" class="success-result">
					<view class="result-item">
						<text class="result-label">授权码 (code):</text>
						<text class="result-value code-value">{{ result.code }}</text>
					</view>

					<view class="result-item">
						<text class="result-label">State参数:</text>
						<text class="result-value state-value">{{ result.state }}</text>
					</view>

					<view class="result-item">
						<text class="result-label">回调地址:</text>
						<text class="result-value uri-value">{{ result.redirect_uri }}</text>
					</view>

						<view class="result-item" v-if="result.message">
						<text class="result-label">消息:</text>
						<text class="result-value message-value">{{ result.message }}</text>
					</view>

					<view class="result-item space-top">
						<button class="btn primary" @click="getUserInfo" :disabled="loading">
							{{ loading ? '正在获取用户信息…' : '获取用户信息' }}
						</button>
					</view>
				</view>

				<view v-else class="error-result">
					<view class="result-item">
						<text class="result-label">错误类型:</text>
						<text class="result-value error-value">{{ result.error }}</text>
					</view>

					<view class="result-item">
						<text class="result-label">错误描述:</text>
						<text class="result-value error-desc-value">{{ result.error_description }}</text>
					</view>
				</view>
			</view>
		</view>

		<view class="log-section" v-if="logs.length > 0">
			<view class="log-header">
				<text class="log-title">调试日志</text>
				<button class="btn btn-small" @click="clearLogs">清除日志</button>
			</view>
			<scroll-view class="log-content" scroll-y>
				<view v-for="(log, index) in logs" :key="index" class="log-item" :class="log.type">
					<text class="log-time">{{ log.time }}</text>
					<text class="log-message">{{ log.message }}</text>
				</view>
			</scroll-view>
		</view>
	</view>
</template>

<script>
	export default {
		name: 'OAuthTest',
		data() {
			return {
				clientId: '',
				result: null,
				resultTime: '',
				isLoading: false,
				loading: false,
				logs: [],
				userInfo: null,
			}
		},
		
		onLoad() {
			// 检查是否有保存的用户信息
			const userInfo = uni.getStorageSync('user_info');
			if (userInfo) {
			  this.userInfo = userInfo;
			}
		},

		computed: {
			resultClass() {
				if (!this.result) return ''
				return this.result.success ? 'success' : 'error'
			},

			resultText() {
				if (!this.result) return ''
				return JSON.stringify(this.result, null, 2)
			}
		},

		mounted() {
			this.fillDefault()
			this.addLog('info', '🎯 页面加载完成，OAuth测试模块已准备就绪')
			this.addLog('info', '📖 实现基于UniMP官方iOS扩展module文档标准')
		},

		methods: {
			// 组件渲染后检查用户头部是否可见
			$nextTickRenderedUser() {
				this.$nextTick(() => {
					if (this.userInfo && this.userInfo.user) {
						this.addLog('info', `🖼️ 用户头部已渲染: ${JSON.stringify(this.userInfo.user)}`)
					}
				})
			},
			// 添加日志
			addLog(type, message) {
				const now = new Date()
				const time =
					`${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`

				this.logs.unshift({
					type,
					time,
					message
				})

				// 限制日志数量
				if (this.logs.length > 50) {
					this.logs = this.logs.slice(0, 50)
				}

				console.log(`[${type.toUpperCase()}] ${message}`)
			},

			// 清除日志
			clearLogs() {
				this.logs = []
				this.addLog('info', '🗑️ 调试日志已清除')
			},

			// 测试OAuth授权
			async testOAuth() {
				// 验证必填字段
				if (!this.clientId || this.clientId.trim() === '') {
					uni.showToast({
						title: '客户端ID不能为空',
						icon: 'none'
					})
					return
				}

				this.isLoading = true
				this.result = null
				this.addLog('info', '🚀 开始测试OAuth授权（按照UniMP官方标准实现）')
				this.addLog('info', `📋 客户端ID: ${this.clientId}`)
				this.addLog('info', '⚙️ 使用官方module扩展方式：uni.requireNativePlugin + 异步回调')

				try {
					// 检查是否在小程序环境中
					if (typeof uni === 'undefined') {
						throw new Error('不在小程序环境中')
					}

					// 获取 栖岛module登录授权服务
					const oauthModule = uni.requireNativePlugin('OAuthDialog')
					if (!oauthModule) {
						// 显示详细的错误信息弹窗
						uni.showModal({
							title: '❌ 模块获取失败',
							content: '无法获取OAuth模块\n\n请检查:\n1. 应用启动时是否弹出"OAuth模块注册成功"\n2. 原生端WXSDKEngine.registerModule是否成功\n3. WX_EXPORT_METHOD是否正确导出',
							showCancel: false
						})
						throw new Error('无法获取OAuth模块，请检查原生端模块是否正确注册')
					}

					this.addLog('info', '✅ 成功获取OAuth模块（使用uni.requireNativePlugin标准方式）')

					// 显示成功获取模块的弹窗确认
					uni.showToast({
						title: '✅ 模块获取成功',
						icon: 'success',
						duration: 2000
					})

					// 准备参数 - 小程序只需要传递client_id，其他由宿主处理
					const options = {
						client_id: this.clientId.trim()
						// 不需要传递state、redirect_uri、scope，宿主会自动处理
					}

					this.addLog('info', `📤 调用异步方法showDialog，参数: ${JSON.stringify(options)}`)
					this.addLog('info', '📋 其他参数（redirect_uri、scope、state）由宿主自动获取和生成')

					// 调用异步方法（按照官方文档标准）
					// testModule.testAsyncFunc(options, callback)
					const result = await this.callOAuthModule(oauthModule, options)

					this.addLog('info', `OAuth模块返回结果: ${JSON.stringify(result)}`)

					// 处理OAuth结果（按照官方文档回调标准）
					if (result && result.success) {
						this.addLog('success', '🎉 OAuth授权测试成功！原生模块调用正常')
						this.addLog('info', `✅ 授权码: ${result.code}`)
						this.addLog('info', `🔑 State参数: ${result.state}`)
						this.addLog('info', `🔗 回调地址: ${result.redirect_uri}`)
						this.addLog('info', '➡️ 即将调用后端接口获取用户信息')

						// 设置成功结果
						this.result = {
							success: true,
							code: result.code,
							state: result.state,
							redirect_uri: result.redirect_uri,
							message: '✅ OAuth授权成功！UniMP模块扩展工作正常'
						}

						this.resultTime = new Date().toLocaleString()

						uni.showToast({
							title: '授权成功',
							icon: 'success'
						})

						// 成功获取授权码后，继续获取用户信息
						this.getUserInfo()
					} else {
						this.addLog('error', `❌ OAuth授权失败: ${result?.error_description || result?.error || '未知错误'}`)

						// 设置失败结果
						this.result = {
							success: false,
							error: result?.error || 'unknown_error',
							error_description: result?.error_description || '未知错误'
						}

						this.resultTime = new Date().toLocaleString()

						uni.showToast({
							title: '授权失败',
							icon: 'none'
						})
						// 授权失败无法获取用户信息
					}

				} catch (error) {
					this.addLog('error', `💥 测试失败: ${error.message}`)
					console.error('OAuth测试失败:', error)

					// 根据错误类型提供具体的解决建议并显示弹窗
					let errorDetail = error.message
					if (error.message.includes('无法获取OAuth模块')) {
						errorDetail += '\n💡 请检查：1.原生端是否正确注册模块 2.WX_EXPORT_METHOD是否正确导出'

						// 显示详细错误弹窗
						uni.showModal({
							title: '🔍 调试信息',
							content: '小程序无法获取OAuth模块\n\n检查步骤:\n1. 应用启动时是否显示"OAuth模块注册成功"弹窗\n2. 如果没有，说明原生端注册失败\n3. 如果有，说明模块名称不匹配',
							showCancel: false
						})
					}

					// 设置错误结果
					this.result = {
						success: false,
						error: 'test_error',
						error_description: errorDetail
					}

					this.resultTime = new Date().toLocaleString()

					uni.showToast({
						title: `测试失败: ${error.message}`,
						icon: 'none'
					})
				} finally {
					this.isLoading = false
				}
			},

			// 调用OAuth模块异步方法（Promise包装，符合官方文档标准）
			callOAuthModule(oauthModule, options) {
				return new Promise((resolve, reject) => {
					try {
						// 按照官方文档调用异步方法：
						// testModule.testAsyncFunc(options, (ret) => { console.log(ret) })
						oauthModule.showDialog(options, (result) => {
							this.addLog('info', `📥 收到原生端回调: ${JSON.stringify(result)}`)
							resolve(result)
						})
					} catch (error) {
						this.addLog('error', `❌ 调用失败: ${error.message}`)
						reject(error)
					}
				})
			},
			getUserInfo() {
			  if (!this.result) {
			    uni.showToast({
			      title: '未获取到授权码',
			      icon: 'none'
			    });
			    return;
			  }
			  
			  if (this.loading) return;
			  this.loading = true;
			  
			  uni.showLoading({
			    title: '获取用户信息中...'
			  });
			  
					  // 构建请求URL,这个Demo的URL使用的是后端/Qidoa_OAuth2.0_Demo/Qidao_Server_ApiDemo/index.php文件，自己部署改为自己的
					  const url = `https://server.tvcloud.top/test/index.php?code=${this.result.code}&state=${this.result.state}`;
					  // 按你要求输出完整代码式日志，包含展开后的参数
					  this.addLog('info', 'const url = `https://server.tvcloud.top/test/index.php?code=' + this.result.code + '&state=' + this.result.state + '`')
			  
					  console.log('请求用户信息:', url);
					  this.addLog('info', `📡 请求用户信息: ${url}`)
			  
			  // 发起请求
			  uni.request({
			    url: url,
			    method: 'GET',
					    success: (res) => {
			      console.log('获取用户信息结果:', res);
					      this.addLog('info', `📥 用户信息响应状态: ${res.statusCode}`)
					      this.addLog('info', `📥 用户信息响应体: ${JSON.stringify(res.data)}`)
			      
						if (res.statusCode === 200) {
			        // 处理响应数据
							if (res.data.success) {
					          const normalized = this.normalizeUserResponse(res.data)
					          this.userInfo = normalized
					          this.$nextTickRenderedUser()
					          this.addLog('success', '👤 已成功获取并规范化用户信息')
			          uni.showToast({
			            title: '获取用户信息成功',
			            icon: 'success'
			          });
			          
			          // 保存用户信息到本地存储
			          uni.setStorageSync('user_info', this.userInfo);
			        } else {
			          uni.showToast({
			            title: res.data.error_description || '获取用户信息失败',
			            icon: 'none'
			          });
			        }
			      } else {
			        uni.showToast({
			          title: '请求失败: ' + res.statusCode,
			          icon: 'none'
			        });
			      }
			    },
			    fail: (err) => {
			      console.error('请求失败:', err);
			      uni.showToast({
			        title: '网络请求失败',
			        icon: 'none'
			      });
			    },
			    complete: () => {
						  this.loading = false;
			      uni.hideLoading();
			    }
			  });
			},

					// 规范化后端返回的用户信息结构，确保有 user.avatar/screenName/uid
					normalizeUserResponse(apiData) {
					  const user = apiData && (apiData.user || apiData.data || apiData.profile || {})
					  const normalizedUser = {
					    avatar: user.avatar || user.avatar_url || user.headimgurl || user.headImgUrl || user.head || '',
					    screenName: user.screenName || user.nickname || user.nick || user.name || user.username || '',
					    uid: user.uid || user.id || user.user_id || user.openid || ''
					  }
					  const result = {
					    ...apiData,
					    user: normalizedUser
					  }
					  // 记录日志帮助排查字段映射
					  this.addLog('info', `用户信息映射结果: ${JSON.stringify(result.user)}`)
					  return result
					},

			// 显示结果
			showResult(result) {
				this.result = result
				const now = new Date()
				this.resultTime =
					`${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}-${now.getDate().toString().padStart(2, '0')} ${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`
			},

			// 清除结果
			clearResult() {
				this.result = null
				this.resultTime = ''
				this.addLog('info', '🧹 测试结果已清除')
			},

			// 填入默认值
			fillDefault() {
				this.clientId = 'test_client'
				this.addLog('info', '📝 已填入默认客户端ID: test_client')
			}
		}
	}
</script>

<style scoped>
	.container {
		padding: 24rpx;
		background-color: #f6f7fb;
		min-height: 100vh;
	}

	/* 始终显示的用户区域，适配小程序渲染 */
	.user-section {
		background: white;
		padding: 24rpx;
		border-radius: 16rpx;
		display: flex;
		align-items: center;
		gap: 20rpx;
		margin-bottom: 20rpx;
	}

	/* 小程序下 <image> 需要明确尺寸 */
	.avatar {
		width: 120rpx;
		height: 120rpx;
		border-radius: 60rpx;
		background-color: #eef2ff;
		border: 2rpx solid #e5e7eb;
	}

	.user-card {
		display: flex;
		align-items: center;
		gap: 20rpx;
		background: #ffffff;
		padding: 24rpx;
		border-radius: 20rpx;
		margin-bottom: 20rpx;
		box-shadow: 0 6rpx 24rpx rgba(0, 0, 0, 0.06);
		border: 2rpx solid #f2f4f8;
	}
	.user-meta { display: flex; flex-direction: column; }
	.user-name { font-size: 32rpx; font-weight: 700; color: #111827; }
	.user-id { font-size: 24rpx; color: #6b7280; margin-top: 4rpx; }

	.header {
		text-align: center;
		margin-bottom: 36rpx;
	}

	.title {
		font-size: 40rpx;
		font-weight: 700;
		color: #0f172a;
		display: block;
		margin-bottom: 8rpx;
		letter-spacing: 1rpx;
	}

	.subtitle {
		font-size: 26rpx;
		color: #64748b;
		display: block;
	}

	.note {
		background-color: #eef2ff;
		border: 2rpx solid #e0e7ff;
		padding: 22rpx 24rpx;
		border-radius: 16rpx;
		margin-bottom: 28rpx;
	}

	.note-text {
		font-size: 26rpx;
		color: #334155;
		line-height: 1.6;
		font-weight: 600;
		display: block;
		margin-bottom: 10rpx;
	}

	.note-detail {
		font-size: 24rpx;
		color: #475569;
		line-height: 1.6;
		display: block;
		margin-bottom: 6rpx;
		padding-left: 0;
	}

	.form-section {
		background: white;
		padding: 30rpx;
		border-radius: 16rpx;
		margin-bottom: 30rpx;
		box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
	}

	.card {
		background: #ffffff;
		padding: 32rpx;
		border-radius: 20rpx;
		margin-bottom: 28rpx;
		box-shadow: 0 8rpx 28rpx rgba(15, 23, 42, 0.06);
		border: 2rpx solid #eef2f7;
	}

	.form-group {
		margin-bottom: 30rpx;
	}

	.label {
		display: block;
		font-size: 28rpx;
		font-weight: bold;
		color: #555;
		margin-bottom: 15rpx;
	}

	.input {
		width: 100%;
		padding: 20rpx;
		border: 2rpx solid #ddd;
		border-radius: 8rpx;
		font-size: 28rpx;
		box-sizing: border-box;
		background-color: #fafafa;
		color: #333;
	}

	.input:focus {
		border-color: #1E88E5;
		background-color: white;
	}

	.input:disabled {
		background-color: #f5f5f5;
		color: #999;
		cursor: not-allowed;
	}

	.example {
		display: block;
		font-size: 22rpx;
		color: #999;
		margin-top: 10rpx;
		font-style: italic;
	}

	.info-section {
		background-color: #e3f2fd;
		padding: 20rpx;
		border-radius: 8rpx;
		border-left: 6rpx solid #2196F3;
	}

	.info-title {
		display: block;
		font-size: 26rpx;
		font-weight: bold;
		color: #1976D2;
		margin-bottom: 15rpx;
	}

	.info-item {
		display: block;
		font-size: 24rpx;
		color: #1565C0;
		line-height: 1.6;
		margin-bottom: 8rpx;
	}

	.actions {
		display: flex;
		flex-direction: column;
		gap: 16rpx;
		margin-bottom: 24rpx;
	}

	.btn {
		width: 100%;
		min-width: 100%;
		padding: 28rpx 32rpx;
		border-radius: 18rpx;
		font-size: 28rpx;
		font-weight: 600;
		text-align: center;
		border-width: 2rpx;
		border-style: solid;
		border-color: transparent;
		box-shadow: 0 8rpx 18rpx rgba(29, 78, 216, 0.08);
		transition: background-color 150ms ease, color 150ms ease, box-shadow 150ms ease, border-color 150ms ease;
	}

	.btn.primary { background-color: #1d4ed8; color: #fff; border-color: #1d4ed8; }
	.btn.primary:disabled { background-color: #93c5fd; color: #f0f9ff; }
	.btn.ghost { background-color: #f8fafc; color: #1d4ed8; border-color: #c7d2fe; }
	.btn.subtle { background-color: #f3f4f6; color: #1f2937; border-color: #e5e7eb; }

	/* pressed/active effect */
	.btn.primary:active { background-color: #1e40af; box-shadow: 0 4rpx 12rpx rgba(30, 64, 175, 0.16); }
	.btn.ghost:active { background-color: #eef2ff; border-color: #a5b4fc; }
	.btn.subtle:active { background-color: #e5e7eb; border-color: #d1d5db; }

	.btn-primary {
		background-color: #1E88E5;
		color: white;
	}

	.btn-primary:disabled {
		background-color: #ccc;
		color: #666;
	}

	.btn-secondary {
		background-color: #666;
		color: white;
	}

	.btn-outline {
		background-color: transparent;
		color: #1E88E5;
		border: 2rpx solid #1E88E5;
	}

	.btn-small {
		padding: 12rpx 22rpx;
		font-size: 22rpx;
		min-width: auto;
		width: auto;
		border-radius: 12rpx;
	}

	.result-section {
		background: white;
		padding: 30rpx;
		border-radius: 16rpx;
		margin-bottom: 30rpx;
		box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);
	}

	.result-header {
		display: flex;
		justify-content: space-between;
		align-items: center;
		margin-bottom: 20rpx;
		padding-bottom: 15rpx;
		border-bottom: 1rpx solid #eee;
	}

	.result-title { font-size: 32rpx; font-weight: 700; letter-spacing: 0.5rpx; }
	.result-title.ok { color: #16a34a; }
	.result-title.err { color: #dc2626; }

	.result-time {
		font-size: 22rpx;
		color: #999;
	}

	.result-content {
		padding: 22rpx;
		border-radius: 16rpx;
		background-color: #f8fafc;
		border-left: 8rpx solid #1d4ed8;
	}

	.result-content.success {
		border-left-color: #28a745;
		background-color: #d4edda;
	}

	.result-content.error {
		border-left-color: #dc3545;
		background-color: #f8d7da;
	}

	.result-text {
		font-family: monospace;
		font-size: 24rpx;
		color: #333;
		line-height: 1.6;
		white-space: pre-wrap;
		word-break: break-all;
	}

	.log-section {
		background: #ffffff;
		padding: 30rpx;
		border-radius: 16rpx;
		box-shadow: 0 6rpx 24rpx rgba(0, 0, 0, 0.06);
		border: 2rpx solid #f2f4f8;
	}

	.log-header {
		display: flex;
		justify-content: space-between;
		align-items: center;
		margin-bottom: 20rpx;
		padding-bottom: 15rpx;
		border-bottom: 1rpx solid #eee;
	}

	.log-title {
		font-size: 32rpx;
		font-weight: bold;
		color: #333;
	}

	.log-content {
		max-height: 400rpx;
	}

	.log-item {
		padding: 16rpx;
		margin-bottom: 10rpx;
		border-radius: 10rpx;
		border-left: 6rpx solid #ddd;
	}

	.hint { background: #f8fafc; border: 2rpx dashed #cbd5e1; border-radius: 12rpx; padding: 16rpx; }
	.hint-title { font-weight: 700; color: #475569; margin-bottom: 8rpx; display: block; }
	.hint-line { color: #64748b; display: block; margin-top: 6rpx; }

	.space-top { margin-top: 16rpx; }

	.log-item.info {
		background-color: #e3f2fd;
		border-left-color: #2196F3;
	}

	.log-item.success {
		background-color: #e8f5e8;
		border-left-color: #4CAF50;
	}

	.log-item.error {
		background-color: #ffebee;
		border-left-color: #f44336;
	}

	.log-item.warning {
		background-color: #fff3e0;
		border-left-color: #ff9800;
	}

	.log-time {
		font-size: 20rpx;
		color: #666;
		margin-right: 15rpx;
	}

	.log-message {
		font-size: 24rpx;
		color: #333;
		line-height: 1.4;
		white-space: nowrap;
		overflow: hidden;
		text-overflow: ellipsis;
		max-width: 100%;
		word-break: normal;
	}
</style>